package com.tengits.mqttmonitor.rnlibs.mqttclient;

import android.text.TextUtils;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.android.service.MqttTraceHandler;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

public class TengitsMqttClient {
    private final int clientRef;
    private String clientLabel;
    private MqttAndroidClient mqttAndroidClient;
    private MqttConnectOptions mqttConnectOptions;
    private final ReactContext mReactContext;

    public TengitsMqttClient(final String label, int clientRef, ReactContext reactContext) {
        this.mReactContext = reactContext;
        this.clientRef = clientRef;
        this.clientLabel = label;
    }

    public int getClientRef() {
        return clientRef;
    }

    public String getClientLabel() {
        return clientLabel;
    }

    private void sendEvent(ReactContext reactContext,
                           String eventName,
                           @Nullable WritableMap params) {
        if(!reactContext.hasActiveReactInstance()) {
            return;
        }
        reactContext
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, params);
    }

    public void createClient(ReadableMap options) {
        mqttAndroidClient = new MqttAndroidClient(mReactContext,
                options.getString("uri"), options.getString("clientId"));
        mqttAndroidClient.setTraceEnabled(options.getBoolean("traceEnable"));
        mqttAndroidClient.setTraceCallback(new MqttTraceHandler() {
            @Override
            public void traceDebug(String tag, String message) {

            }

            @Override
            public void traceError(String tag, String message) {

            }

            @Override
            public void traceException(String tag, String message, Exception e) {

            }
        });
        mqttAndroidClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                WritableMap map = Arguments.createMap();
                map.putString("event", "connect");
                map.putInt("clientRef", clientRef);
                map.putString("message", String.valueOf(reconnect));
                sendEvent(mReactContext, "mqtt_events", map);
            }

            @Override
            public void connectionLost(Throwable cause) {
                WritableMap map = Arguments.createMap();
                map.putString("event", "closed");
                map.putInt("clientRef", clientRef);
                map.putString("message", "connectionLost");
                sendEvent(mReactContext, "mqtt_events", map);
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                WritableMap data = Arguments.createMap();
                data.putString("topic", topic);
                data.putString("data", new String(message.getPayload()));
                data.putInt("messageId", message.getId());
                data.putInt("qos", message.getQos());
                data.putBoolean("retain", message.isRetained());
                data.putBoolean("duplicate", message.isDuplicate());

                WritableMap map = Arguments.createMap();
                map.putString("event", "message");
                map.putInt("clientRef", clientRef);
                map.putMap("message", data);

                sendEvent(mReactContext, "mqtt_events", map);
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {

            }
        });
        mqttConnectOptions = new MqttConnectOptions();
        String userName = options.getString("userName");
        String passWord = options.getString("password");
        mqttConnectOptions.setCleanSession(options.getBoolean("cleanSession"));
        mqttConnectOptions.setAutomaticReconnect(options.getBoolean("autoReconnect"));
        mqttConnectOptions.setConnectionTimeout(options.getInt("connectTimeout"));
        mqttConnectOptions.setKeepAliveInterval(options.getInt("keepAliveInterval"));
        mqttConnectOptions.setMqttVersion(options.getInt("mqttVersion"));
        mqttConnectOptions.setMaxReconnectDelay(options.getInt("maxReconnectDelay"));
        if(!TextUtils.isEmpty(userName)) {
            mqttConnectOptions.setUserName(userName);
        }
        if(!TextUtils.isEmpty(passWord)) {
            mqttConnectOptions.setPassword(passWord.toCharArray());
        }
    }

    public void connect(Promise promise) {
        try {
            mqttAndroidClient.connect(mqttConnectOptions, mReactContext, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
//                    WritableMap map = Arguments.createMap();
//                    map.putString("event", "connect");
//                    map.putInt("clientRef", clientRef);
//                    map.putString("message", "connectSuccess");
//                    sendEvent(mReactContext, "mqtt_events", map);
                    promise.resolve(0);
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    WritableMap map = Arguments.createMap();
                    map.putString("event", "closed");
                    map.putInt("clientRef", clientRef);
                    map.putString("message", "connectFail");
                    sendEvent(mReactContext, "mqtt_events", map);
                    promise.resolve(-1);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            WritableMap map = Arguments.createMap();
            map.putString("event", "error");
            map.putInt("clientRef", clientRef);
            map.putString("message", "connectExcept"+e.getMessage());
            sendEvent(mReactContext, "mqtt_events", map);
            promise.resolve(-1);
        }
    }

    public MqttAndroidClient getMqttClient() {
        return this.mqttAndroidClient;
    }
}
